package pri.damai.xiaowu.common.core.validate;

import pri.damai.xiaowu.common.core.enums.base.IntegerEnumerator;

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.HashSet;
import java.util.Set;

/**
 * 自定义参数验证注解, 用于验证枚举类的 int 值作为参数
 * @Desc
 * @Author DaMai
 * @Date 2021/5/19 14:46
 * 但行好事，莫问前程。
 */
@Retention(RetentionPolicy.RUNTIME)
// 这个注解是validation提供的，用于指定对应的校验器
@Constraint(validatedBy = EnumIntValue.EnumIntValueConstraintValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface EnumIntValue {

    String message() default "参数不合法";

    /**
     * @return 指定分组
     */
    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * @return 指定枚举类型，参数值必须是这个枚举类型中的值
     */
    Class<? extends IntegerEnumerator> enumClass();

    /**
     * @return 是否可以为 null
     */
    boolean nullable() default false;

    /**
     * @return 需要排除的值
     */
    int[] exclusion() default {};

    class EnumIntValueConstraintValidator implements ConstraintValidator<EnumIntValue, Integer> {

        private boolean nullable;

        private Class<? extends IntegerEnumerator> enumClass;

        private int[] exclusion;

        private Set<Integer> vals;


        /**
         * 初始化校验器，获取注解中的属性并记录下来
         */
        @Override
        public void initialize(EnumIntValue constraintAnnotation) {
            this.nullable = constraintAnnotation.nullable();
            this.enumClass = constraintAnnotation.enumClass();
            this.exclusion = constraintAnnotation.exclusion();

            vals = new HashSet<>();
            IntegerEnumerator[] enumConstants = enumClass.getEnumConstants();
            for (IntegerEnumerator iEnum : enumConstants) {
                vals.add(iEnum.code());
            }
            for (int i : exclusion) {
                vals.remove(i);
            }
        }


        /**
         * 校验参数是否合法
         */
        @Override
        public boolean isValid(Integer param, ConstraintValidatorContext context) {
            if (nullable && param == null) {
                return true;
            }

            return vals.contains(param);
        }

    }

}
